`include "mycpu.h"
module id_stage(
    input clk,
    input reset,
    //EXE stage allowin
    input es_allowin,
    //ID stage allowin
    output ds_allowin,
    //IF to ID stage
    input fs_to_ds_valid,
    input [`FS_TO_DS_WD - 1:0] fs_to_ds_bus,
    //ID to EXE stage
    output ds_to_es_valid,
    output [`DS_TO_ES_WD - 1:0] ds_to_es_bus,
    //branch bus
    output [`BR_BUS_WD - 1:0] br_bus,
    //WB to regfile
    input [`WS_TO_RF_WD - 1:0] ws_to_rf_bus,
    input out_ws_valid,
    //EXE dest
    input [`ES_TO_MS_WD - 1:0] es_to_ms_bus,
    input out_es_valid,
    //MEM dest
    input [`MS_TO_WS_WD - 1:0] ms_to_ws_bus,
    input out_ms_valid
);

reg ds_valid;
wire ds_ready_go;
wire rf_depend;

assign ds_ready_go = rf_depend ? 1'b0: 1'b1;

wire        br_taken;

always @(posedge clk) begin
    if(reset)
        ds_valid <= 1'b0;
    else if(br_taken && ds_ready_go) begin
        ds_valid <= 1'b0;
    end
    else if(ds_allowin)
        ds_valid <= fs_to_ds_valid;
end


//IF to ID
    reg [`FS_TO_DS_WD - 1:0] fs_to_ds_bus_r;

    assign ds_allowin = !ds_valid || ds_ready_go && es_allowin;

    always @(posedge clk) begin
        if(ds_allowin && fs_to_ds_valid) 
            fs_to_ds_bus_r <= fs_to_ds_bus;
    end

wire [31:0] ds_inst;
wire [31:0] ds_pc;
wire [31:0] fs_pc;
assign fs_pc[31:0] = fs_to_ds_bus[31:0];
assign {ds_inst[31:0], ds_pc[31:0]} = fs_to_ds_bus_r[`FS_TO_DS_WD - 1:0];
wire        rf_we   ;
wire [ 4:0] rf_waddr;
wire [31:0] rf_wdata;
assign {rf_we, rf_waddr[4:0], rf_wdata[31:0]} = ws_to_rf_bus;

//ID stage
//declare

    wire [31:0] br_target;

    wire [11:0] alu_op;
    wire        load_op;
    wire        src1_is_pc;
    wire        src2_is_imm;
    wire        res_from_mem;
    wire        dst_is_r1;
    wire        gr_we;
    wire        mem_we;
    wire        src_reg_is_rd;
    wire        rj_eq_rd;
    wire        rj_lt_rd;
    wire        rj_ltu_rd;
    wire [4: 0] dest;
    wire [31:0] rj_value;
    wire [31:0] rkd_value;
    wire [31:0] imm;
    wire [31:0] br_offs;
    wire [31:0] jirl_offs;

    wire [ 5:0] op_31_26;
    wire [ 3:0] op_25_22;
    wire [ 1:0] op_21_20;
    wire [ 4:0] op_19_15;
    wire [ 4:0] rd;
    wire [ 4:0] rj;
    wire [ 4:0] rk;
    wire [11:0] i12;
    wire [19:0] i20;
    wire [15:0] i16;
    wire [25:0] i26;

    wire [63:0] op_31_26_d;
    wire [15:0] op_25_22_d;
    wire [ 3:0] op_21_20_d;
    wire [31:0] op_19_15_d;

    wire        inst_add_w;
    wire        inst_sub_w;
    wire        inst_slt;
    wire        inst_sltu;
    wire        inst_nor;
    wire        inst_and;
    wire        inst_or;
    wire        inst_xor;
    wire        inst_slli_w;
    wire        inst_srli_w;
    wire        inst_srai_w;
    wire        inst_addi_w;
    wire        inst_ld_w;
    wire        inst_st_w;
    wire        inst_jirl;
    wire        inst_b;
    wire        inst_bl;
    wire        inst_beq;
    wire        inst_bne;
    wire        inst_lu12i_w;
    //exp10
    wire        inst_slti;
    wire        inst_sltui;
    wire        inst_andi;
    wire        inst_ori;
    wire        inst_xori;
    wire        inst_sll_w;
    wire        inst_srl_w;
    wire        inst_sra_w;
    wire        inst_pcaddu12i;
    wire        inst_mul_w;
    wire        inst_mulh_w;
    wire        inst_mulh_wu;
    wire        inst_div_w;
    wire        inst_div_wu;
    wire        inst_mod_w;
    wire        inst_mod_wu;
    wire        op_mulh;
    wire        op_mul;
    wire        op_div;
    wire        op_mod;
    wire        op_unsigned; //for mul,mulh,div,divu,mod,modu
    //exp11
    wire        inst_blt;
    wire        inst_bge;
    wire        inst_bltu;
    wire        inst_bgeu;
    wire        inst_ld_b;
    wire        inst_ld_h;
    wire        inst_ld_bu;
    wire        inst_ld_hu;
    wire        inst_st_b;
    wire        inst_st_h;
    wire        ld_unsigned;  
    wire        op_ls_b;
    wire        op_ls_h;

    wire        need_ui5;
    wire        need_ui12;
    wire        need_si12;
    wire        need_si16;
    wire        need_si20;
    wire        need_si26;
    wire        src2_is_4;

    wire [ 4:0] rf_raddr1;
    wire [31:0] rf_rdata1;
    wire [ 4:0] rf_raddr2;
    wire [31:0] rf_rdata2;
    
    wire rj_is_zero, rkd_is_zero;
    wire rj_is_depend_es, rj_is_depend_ms, rj_is_depend_ws;
    wire rkd_is_depend_es, rkd_is_depend_ms, rkd_is_depend_ws;
    wire rj_depend_from_mem, rkd_depend_from_mem;



//inst segements
    assign op_31_26  = ds_inst[31:26];
    assign op_25_22  = ds_inst[25:22];
    assign op_21_20  = ds_inst[21:20];
    assign op_19_15  = ds_inst[19:15];
    assign rd   = ds_inst[ 4: 0];
    assign rj   = ds_inst[ 9: 5];
    assign rk   = ds_inst[14:10];
//imm
    assign i12  = ds_inst[21:10];
    assign i20  = ds_inst[24: 5];
    assign i16  = ds_inst[25:10];
    assign i26  = {ds_inst[ 9: 0], ds_inst[25:10]};
//decoder
    decoder_6_64 u_dec0(.in(op_31_26 ), .out(op_31_26_d ));
    decoder_4_16 u_dec1(.in(op_25_22 ), .out(op_25_22_d ));
    decoder_2_4  u_dec2(.in(op_21_20 ), .out(op_21_20_d ));
    decoder_5_32 u_dec3(.in(op_19_15 ), .out(op_19_15_d ));
//decode inst
    assign inst_add_w  = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h00];
    assign inst_sub_w  = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h02];
    assign inst_slt    = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h04];
    assign inst_sltu   = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h05];
    assign inst_nor    = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h08];
    assign inst_and    = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h09];
    assign inst_or     = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h0a];
    assign inst_xor    = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h0b];
    assign inst_slli_w = op_31_26_d[6'h00] & op_25_22_d[4'h1] & op_21_20_d[2'h0] & op_19_15_d[5'h01];
    assign inst_srli_w = op_31_26_d[6'h00] & op_25_22_d[4'h1] & op_21_20_d[2'h0] & op_19_15_d[5'h09];
    assign inst_srai_w = op_31_26_d[6'h00] & op_25_22_d[4'h1] & op_21_20_d[2'h0] & op_19_15_d[5'h11];
    assign inst_addi_w = op_31_26_d[6'h00] & op_25_22_d[4'ha];
    assign inst_ld_w   = op_31_26_d[6'h0a] & op_25_22_d[4'h2];
    assign inst_st_w   = op_31_26_d[6'h0a] & op_25_22_d[4'h6];
    assign inst_jirl   = op_31_26_d[6'h13];
    assign inst_b      = op_31_26_d[6'h14];
    assign inst_bl     = op_31_26_d[6'h15];
    assign inst_beq    = op_31_26_d[6'h16];
    assign inst_bne    = op_31_26_d[6'h17];
    assign inst_lu12i_w = op_31_26_d[6'h05] & ~ds_inst[25];
    //exp10
    assign inst_slti = op_31_26_d[6'h00] & op_25_22_d[4'h8];
    assign inst_sltui = op_31_26_d[6'h00] & op_25_22_d[4'h9];
    assign inst_andi = op_31_26_d[6'h00] & op_25_22_d[4'hd];
    assign inst_ori = op_31_26_d[6'h00] & op_25_22_d[4'he];
    assign inst_xori = op_31_26_d[6'h00] & op_25_22_d[4'hf];
    assign inst_sll_w = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h0e]; 
    assign inst_srl_w = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h0f];
    assign inst_sra_w = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h10];
    assign inst_pcaddu12i = op_31_26_d[6'h07] & ~ds_inst[25];
    assign inst_mul_w = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h18];
    assign inst_mulh_w = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h19];
    assign inst_mulh_wu = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h1] & op_19_15_d[5'h1a];
    assign inst_div_w = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h2] & op_19_15_d[5'h00];
    assign inst_div_wu = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h2] & op_19_15_d[5'h02];
    assign inst_mod_w = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h2] & op_19_15_d[5'h01];
    assign inst_mod_wu = op_31_26_d[6'h00] & op_25_22_d[4'h0] & op_21_20_d[2'h2] & op_19_15_d[5'h03];
    assign op_mul = inst_mul_w;
    assign op_mulh = inst_mulh_w | inst_mulh_wu;
    assign op_div = inst_div_w | inst_div_wu;
    assign op_mod = inst_mod_w | inst_mod_wu;
    assign op_unsigned = inst_mulh_wu | inst_div_wu | inst_mod_wu;
    //exp11
    assign inst_blt = op_31_26_d[6'h18];
    assign inst_bge = op_31_26_d[6'h19];
    assign inst_bltu = op_31_26_d[6'h1a];
    assign inst_bgeu = op_31_26_d[6'h1b];
    assign inst_ld_b = op_31_26_d[6'h0a] & op_25_22_d[4'h0];
    assign inst_ld_h = op_31_26_d[6'h0a] & op_25_22_d[4'h1];
    assign inst_ld_bu = op_31_26_d[6'h0a] & op_25_22_d[4'h8];
    assign inst_ld_hu = op_31_26_d[6'h0a] & op_25_22_d[4'h9];
    assign inst_st_b = op_31_26_d[6'h0a] & op_25_22_d[4'h4];
    assign inst_st_h = op_31_26_d[6'h0a] & op_25_22_d[4'h5];
    assign op_ls_b = inst_ld_b | inst_ld_bu | inst_st_b;
    assign op_ls_h = inst_ld_h | inst_ld_hu | inst_st_h;
    assign ld_unsigned = inst_ld_bu | inst_ld_hu;
//aluop
    assign alu_op[ 0] = inst_add_w | inst_addi_w | inst_ld_w | inst_st_w
                        | inst_jirl | inst_bl | inst_pcaddu12i | inst_ld_b
                        | inst_ld_bu | inst_ld_h | inst_ld_hu | inst_st_b
                        | inst_st_h;
    assign alu_op[ 1] = inst_sub_w;
    assign alu_op[ 2] = inst_slt | inst_slti;
    assign alu_op[ 3] = inst_sltu | inst_sltui;
    assign alu_op[ 4] = inst_and | inst_andi;
    assign alu_op[ 5] = inst_nor;
    assign alu_op[ 6] = inst_or | inst_ori;
    assign alu_op[ 7] = inst_xor | inst_xori;
    assign alu_op[ 8] = inst_slli_w | inst_sll_w;
    assign alu_op[ 9] = inst_srli_w | inst_srl_w;
    assign alu_op[10] = inst_srai_w | inst_sra_w;
    assign alu_op[11] = inst_lu12i_w;
//control unit
    assign need_ui5   =  inst_slli_w | inst_srli_w | inst_srai_w;
    assign need_si12  =  inst_addi_w | inst_ld_w | inst_st_w | inst_slti | inst_sltui |
                         inst_ld_b | inst_ld_h | inst_ld_bu | inst_ld_hu | inst_st_b | 
                         inst_st_h;
    assign need_si16  =  inst_jirl | inst_beq | inst_bne | inst_blt | inst_bge |
                         inst_bltu | inst_bgeu;
    assign need_si20  =  inst_lu12i_w | inst_pcaddu12i;
    assign need_si26  =  inst_b | inst_bl;
    assign need_ui12  =  inst_andi | inst_ori | inst_xori;
    assign src2_is_4  =  inst_jirl | inst_bl;

    assign imm = src2_is_4 ? 32'h4                      :
                need_si20 ? {i20[19:0], 12'b0}         :
                need_ui12 ? {20'b0, i12[11:0]}         :
    /*need_ui5 || need_si12*/{{20{i12[11]}}, i12[11:0]} ;

    assign br_offs = need_si26 ? {{ 4{i26[25]}}, i26[25:0], 2'b0} :
                                {{14{i16[15]}}, i16[15:0], 2'b0} ;

    assign jirl_offs = {{14{i16[15]}}, i16[15:0], 2'b0};

    assign src_reg_is_rd = inst_beq | inst_bne | inst_st_w | inst_st_b | inst_st_h |
                           inst_blt | inst_bge | inst_bltu | inst_bgeu;

    assign src1_is_pc    = inst_jirl | inst_bl | inst_pcaddu12i;

    assign src2_is_imm   = inst_slli_w |
                        inst_srli_w |
                        inst_srai_w |
                        inst_addi_w |
                        inst_ld_w   |
                        inst_ld_b   |
                        inst_ld_bu  |
                        inst_ld_h   |
                        inst_ld_hu  |
                        inst_st_w   |
                        inst_st_b   |
                        inst_st_h   |
                        inst_lu12i_w|
                        inst_jirl   |
                        inst_bl     |
                        inst_slti   |
                        inst_sltui  |
                        inst_andi   |
                        inst_ori    |
                        inst_xori   |
                        inst_pcaddu12i;

    assign res_from_mem  = inst_ld_w | inst_ld_b | inst_ld_bu | inst_ld_h | inst_ld_hu;
    assign dst_is_r1     = inst_bl;
    assign gr_we         = ~inst_st_w & ~inst_beq & ~inst_bne & ~inst_b & ~inst_st_b &
                           ~inst_st_h & ~inst_blt & ~inst_bge & ~inst_bltu & ~inst_bgeu;
    assign mem_we        = inst_st_w | inst_st_b | inst_st_h;
    assign dest          = dst_is_r1 ? 5'd1 : rd;
    assign rj_eq_rd = rj_value == rkd_value;
    assign rj_lt_rd = $signed(rj_value) < $signed(rkd_value);
    assign rj_ltu_rd = rj_value < rkd_value;
    assign br_taken =  (inst_beq  &&  rj_eq_rd
                     || inst_bne  && !rj_eq_rd
                     || inst_blt  &&  rj_lt_rd
                     || inst_bge  && ~rj_lt_rd
                     || inst_bltu &&  rj_ltu_rd
                     || inst_bgeu && ~rj_ltu_rd
                     || inst_jirl
                     || inst_bl
                     || inst_b) && ds_valid;
    assign br_target = (inst_beq || inst_bne || inst_bl || inst_b || inst_blt ||
                        inst_bge || inst_bltu || inst_bgeu) ? (ds_pc + br_offs) :
                                                   /*inst_jirl*/ (rj_value + jirl_offs);
//regfile
    assign rf_raddr1 = rj;
    assign rf_raddr2 = src_reg_is_rd ? rd :rk;
    regfile u_regfile(
        .clk (clk),
        .raddr1 (rf_raddr1),
        .rdata1 (rf_rdata1),
        .raddr2 (rf_raddr2),
        .rdata2 (rf_rdata2),
        .we     (rf_we    ),
        .waddr  (rf_waddr ),
        .wdata  (rf_wdata )
    );

    //if rf_addr1 is zero
    assign rj_is_zero = rf_raddr1 == 5'b0;
    //es_to_ms_bus dest[4:0], gr_we[5:5], res_from_mem[6:6], alu_result[38:7]
    assign rj_is_depend_es = out_es_valid & (es_to_ms_bus[4:0] == rf_raddr1) & es_to_ms_bus[5];
    assign rj_depend_from_mem = rj_is_depend_es & es_to_ms_bus[6];
    //ms_to_ws_bus final_result[31:0], dest[36:32], gr_we[37:37]
    assign rj_is_depend_ms = ~rj_is_depend_es & out_ms_valid & (ms_to_ws_bus[36:32] == rf_raddr1) & ms_to_ws_bus[37];
    //wr_to_rf_bus ws_final_result[31:0], ws_dest[36:32], ws_rf_we[37:37]
    assign rj_is_depend_ws = ~rj_is_depend_ms & ~rj_is_depend_es & out_ws_valid & ws_to_rf_bus[37] & (ws_to_rf_bus[36:32] == rf_raddr1);

    assign rj_value  = {32{~rj_is_zero & rj_is_depend_es}} & es_to_ms_bus[38:7] |
                       {32{~rj_is_zero & rj_is_depend_ms}} & ms_to_ws_bus[31:0] |
                       {32{~rj_is_zero & rj_is_depend_ws}} & ws_to_rf_bus[31:0] |
                       {32{~rj_is_depend_es & ~rj_is_depend_ms & ~rj_is_depend_ws}} & rf_rdata1[31:0];
    //assign rj_value = rf_rdata1;

    //if rf_addr2 is zero
    assign rkd_is_zero = rf_raddr2 == 5'b0;
    //es_to_ms_bus dest[4:0], gr_we[5:5], res_from_mem[6:6], alu_result[38:7]
    assign rkd_is_depend_es = out_es_valid & (es_to_ms_bus[4:0] == rf_raddr2) & es_to_ms_bus[5];
    assign rkd_depend_from_mem = rkd_is_depend_es & es_to_ms_bus[6];
    //ms_to_ws_bus final_result[31:0], dest[36:32], gr_we[37:37]
    assign rkd_is_depend_ms = ~rkd_is_depend_es & out_ms_valid && (ms_to_ws_bus[36:32] == rf_raddr2) & ms_to_ws_bus[37];
    //wr_to_rf_bus ws_final_result[31:0], ws_dest[36:32], ws_rf_we[37:37]
    assign rkd_is_depend_ws = ~rkd_is_depend_es & ~rkd_is_depend_ms & out_ws_valid & ws_to_rf_bus[37] & (ws_to_rf_bus[36:32] == rf_raddr2);

    assign rkd_value = {32{~rkd_is_zero & rkd_is_depend_es}} & es_to_ms_bus[38:7] |
                       {32{~rkd_is_zero & rkd_is_depend_ms}} & ms_to_ws_bus[31:0] |
                       {32{~rkd_is_zero & rkd_is_depend_ws}} & ws_to_rf_bus[31:0] |
                       {32{~rkd_is_depend_es & ~rkd_is_depend_ms & ~rkd_is_depend_ws}} & rf_rdata2[31:0];
    //assign rkd_value = rf_rdata2;

    assign rf_depend = (~rj_is_zero & rj_depend_from_mem) |
                       (~rkd_is_zero & rkd_depend_from_mem);
    

//ID to EXE
    assign ds_to_es_valid = ds_valid && ds_ready_go;
    assign ds_to_es_bus[`DS_TO_ES_WD - 1:0] = {
        ld_unsigned,        //157:157    
        op_ls_b,            //156:156
        op_ls_h,            //155:155
        op_unsigned,        //154:154
        op_mul,             //153:153
        op_mulh,            //152:152
        op_div,             //151:151
        op_mod,             //150:150
        ds_pc[31:0],        //149:118
        rj_value[31:0],     //117:86
        rkd_value[31:0],    //85:54
        imm[31:0],          //53:22
        src1_is_pc,         //21:21
        src2_is_imm,        //20:20     
        res_from_mem,       //19:19
        gr_we,              //18:18
        mem_we,             //17:17
        dest[4:0],          //16:12
        alu_op[11:0]        //11:0
    };
//branch bus
    assign br_bus[32:0] = {br_taken, br_target[31:0]};

endmodule